package cn.ym.ymcc.service.impl;

import cn.ym.ymcc.doc.CourseDoc;
import cn.ym.ymcc.mapper.HighlightResultMapper;
import cn.ym.ymcc.query.ESQuery;
import cn.ym.ymcc.repository.SearchRepository;
import cn.ym.ymcc.result.PageList;
import cn.ym.ymcc.service.ISearchService;
import cn.ym.ymcc.vo.AggPageList;
import cn.ym.ymcc.vo.BucketVo;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.InnerHitBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service
@Slf4j
public class SearchServiceImpl implements ISearchService {

    @Autowired
    private SearchRepository searchRepository;
    @Autowired
    private ElasticsearchRestTemplate elasticsearchRestTemplate;
    @Autowired
    private HighlightResultMapper highlightResultMapper;
    /**
     * 保存课程到es里面
     * @param courseDoc
     * @return
     */
    @Override
    public void save(CourseDoc courseDoc) {
        searchRepository.save(courseDoc);
    }
    /**
     * es查询列表
     * @param esQuery
     * @return
     */
    @Override
    public PageList<CourseDoc> search(ESQuery esQuery) {
        //1.创建一个本机查询构建起builder
        NativeSearchQueryBuilder builder = new NativeSearchQueryBuilder();
        //--------------加条件----------------------------------------------
        //2.添加分页相关的数据
        builder.withPageable(PageRequest.of(esQuery.getCurrentPage()-1,esQuery.getPageSize()));
        //3.添加排序相关的数据
        if (esQuery.getSortField()!=null && esQuery.getSortField().length()>0){
            //3.1按什么字段排序：排序条件
            String orderField =  choseOrderName(esQuery.getSortField());
            //3.2按那种方式排序：排序规则
            SortOrder sortOrder = esQuery.getSortType().equals("asc") ? SortOrder.ASC : SortOrder.DESC;
            //3.3把条件放进builder里面
            builder.withSort(SortBuilders.fieldSort(orderField).order(sortOrder));
        }
        //-------------------------添加查询相关条件---------------------------------------------------
        //4.创建组合条件查询
        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
        //4.1拿到es里面name关键字分词
        if (StringUtils.hasLength(esQuery.getKeyword())){
            boolQuery.must(QueryBuilders.matchQuery("name",esQuery.getKeyword()));
        }
        //4.2过滤课程名等级条件
        if (StringUtils.hasLength(esQuery.getChargeName())){
            boolQuery.filter(QueryBuilders.termQuery("chargeName",esQuery.getChargeName()));
        }
        //4.3过滤等级等级条件
        if (StringUtils.hasLength(esQuery.getGradeName())){
            boolQuery.filter(QueryBuilders.termQuery("gradeName",esQuery.getGradeName()));
        }
        //4.4课程类型id不能为null
        if (esQuery.getCourseTypeId() !=null){
            boolQuery.filter(QueryBuilders.termQuery("courseTypeId",esQuery.getCourseTypeId()));
        }
        //4.5最小价格不能为null
        if (esQuery.getPriceMin()!=null){
            boolQuery.filter(QueryBuilders.rangeQuery("price").gte(esQuery.getPriceMin()));
        }
        //4.6最大价格不能为null
        if (esQuery.getPriceMax()!=null){
            boolQuery.filter(QueryBuilders.rangeQuery("price").lte(esQuery.getPriceMax()));
        }
        //4.7把这些条件放进builer里面
        builder.withQuery(boolQuery);
        //-----------------------------添加高亮-----------------------------------------------------
        //5.对指定的字段添加高亮
        HighlightBuilder.Field field = new HighlightBuilder.Field("name").preTags("<span style='color:red'>").postTags("</span>");
        builder.withHighlightFields(field);
        //-------------------------聚合相关条件[等级，收费规则]-------------------------------------------------------
        //6.等级条件
        TermsAggregationBuilder gradeNameAggField = AggregationBuilders.terms("gradeNameAgg").field("gradeName");
        //7.收费规则
        TermsAggregationBuilder chargeNameField = AggregationBuilders.terms("chargeNameAgg").field("chargeName");
        builder.addAggregation(gradeNameAggField).addAggregation(chargeNameField);

        //----------------------------发起查询--------------------------------
        NativeSearchQuery searchQuery = builder.build();
        //Page<CourseDoc> search = searchRepository.search(searchQuery);
        //开始查询，把自定义的结果值映射丢给Springboot
        AggregatedPage<CourseDoc> search = elasticsearchRestTemplate.queryForPage(searchQuery, CourseDoc.class,highlightResultMapper);

        //7.----------------------获取集合查询的结果集--------------------------------
        List<Aggregation> aggregations = search.getAggregations().asList();
        //8.封装最终返回的数据结果集
        HashMap<String, List<BucketVo>> map = new HashMap<>();
        //9.遍历结果集
        aggregations.forEach(aggregation -> {
            ParsedStringTerms terms = (ParsedStringTerms) aggregation;
            String name = terms.getName();//聚合的名字
            //获取结果集信息
            List<? extends Terms.Bucket> buckets = terms.getBuckets();
            //每组数据的list
            List<BucketVo> bucketVos = new ArrayList<>();
            buckets.forEach(v->{
                BucketVo bucketVo = new BucketVo(v.getKeyAsString(), v.getDocCount());
                bucketVos.add(bucketVo);
            });
            map.put(name,bucketVos);
        });
        //返回查询结果
        return new AggPageList<>(search.getTotalElements(),search.getContent(),map);
    }

    /**
     * 前端选择的排序方式
     * @param sortField
     * @return
     */
    private String choseOrderName(String sortField) {
        switch (sortField){
            case "xp":return "onlineTime";
            case "xl":return "saleCount";
            case "pl":return "commentCount";
            case "jg":return "price";
            case "rq":return "viewCount";
        }
       return "onlineTime";
    }





}
